/**********
Project 7, Virtual Machine
Mike Tigas
Ryan VanMaele
**********
file info here
*/


#include <stdlib.h>
#include <stdio.h>
#include "operations.h"
#include "operations.c"
#include "parselex.h"
#include "parselex.c"
#include "tokenlist.h"
#include "tokenlist.c"



void makeProgram(vm_Instruction *program, struct tokenlist *tlist) {
	//since our pointer starts at 0
	//and our incrementer happens at the top of the loop,
	//we're starting at -1; lame, i know
	int pointer = -1;
	int token_in_line = 0;
	
	//start at the first node
	tlist->current = tlist->head;
	
	//go through the entire token list
	while (tlist->current != NULL) {
		if(tlist->current->type == OPCODE) {
			//if it's an opcode, we start over (since we only use one opcode per operation)

			// Reset our counters.
			token_in_line = 0;

			// Move on to the next instruction.
			pointer++;

			// Initialize this instruction.
			program[pointer].a = 0;
			program[pointer].b = 0;
			program[pointer].c = 0;
			program[pointer].f = 0.0;
			program[pointer].is_floatop = 0;

			//we set the operation to a do-nothing function by default
			program[pointer].operation = vm_NUL;
			program[pointer].floatop = vm_NUL_float;

			// Get the opcode instruction
			if (tlist->current->v.op == MOV)
				program[pointer].operation = vm_MOV;
			else if (tlist->current->v.op == ADD)
				program[pointer].operation = vm_ADD;
			else if (tlist->current->v.op == SUB)
				program[pointer].operation = vm_SUB;
			else if (tlist->current->v.op == MULT)
				program[pointer].operation = vm_MULT;
			else if (tlist->current->v.op == DIV)
				program[pointer].operation = vm_DIV;
			else if (tlist->current->v.op == COPY)
				program[pointer].operation = vm_COPY;
			else if (tlist->current->v.op == JMP)
				program[pointer].operation = vm_JMP;
			else if (tlist->current->v.op == JE)
				program[pointer].operation = vm_JE;
			else if (tlist->current->v.op == JNE)
				program[pointer].operation = vm_JNE;
			else if (tlist->current->v.op == SJMP)
				program[pointer].operation = vm_SJMP;
			else if (tlist->current->v.op == SWAP)
				program[pointer].operation = vm_SWAP;
			else if (tlist->current->v.op == DISP)
				program[pointer].operation = vm_DISP;
			token_in_line++;
		}
		else if(tlist->current->type == REGISTER) {
			//registers are represented by the integer position of the register (0-9)

			//convert token 1, 2, 3 into respective operands a, b, or c
			if (token_in_line==1)
				program[pointer].a = tlist->current->v.r;
			else if (token_in_line==2)
				program[pointer].b = tlist->current->v.r;
			else if (token_in_line==3)
				program[pointer].c = tlist->current->v.r;
			token_in_line++;
		}
		else if(tlist->current->type == INTEGER) {
			// similar to above

			if (token_in_line==1)
				program[pointer].a = tlist->current->v.i;
			else if (token_in_line==2)
				program[pointer].b = tlist->current->v.i;
			else if (token_in_line==3)
				program[pointer].c = tlist->current->v.i;
			token_in_line++;
		}
		else if((tlist->current->type == FLOAT)) {
			// similar to above, but i take a shortcut
			// knowing that the only "true" float operation is MOV
			// since other operations work on registers anyway

			program[pointer].f = tlist->current->v.f;
			program[pointer].is_floatop = 1;
			program[pointer].operation = vm_NUL;
			program[pointer].floatop = vm_MOV_float;
			token_in_line++;
		}
		//go to next node
		tlist->current = tlist->current->next;
	}
}

// Runs a program (vm_Instruction array)
void runProgram(vm_Instruction *program) {
	vm_State vm;

	vm.pointer = 0;
	while ((program[vm.pointer].operation != NULL)&&(program[vm.pointer].floatop != NULL)) {
		//if we're using the floating point operation for this pointer
		if (program[vm.pointer].is_floatop == 1) {
			(*program[vm.pointer].floatop)(&vm,program[vm.pointer].a,program[vm.pointer].f);
		}
		//otherwise, we use the standard operation associated with this pointer
		else {
			(*program[vm.pointer].operation)(&vm,program[vm.pointer].a,program[vm.pointer].b,program[vm.pointer].c);
		}
		vm.pointer++;
	}
}

int main(int argc, char * argv[]) {
	if (argc < 2) {
		printf("No input file specified.\n\nUsage: %s [input_file]\n\n",argv[0]);
		return EXIT_FAILURE;
	}

	int numerr = 0;
	int size = 1;
	vm_Instruction * program;

	/* create a new list */
	struct tokenlist *thelist = newlist();

	/* call line parser */
	numerr = parse_line(argv[1],thelist);

	if (numerr == 0) {
		/* RUN PROGRAM: */
		/* Parse the tokens into a vm_Instruction array. */
		size = lines_in_prog();
		program = malloc(size * (sizeof(int)*4+sizeof(double)+128)); //adding arbitrary size of 128, heh
		makeProgram(program,thelist);

		/* Create and run a VM state to start processing instructions. */
		runProgram(program);

		/* Free the malloc'd list & program */
		kill_list(thelist);
		free(program);
		return EXIT_SUCCESS;
	}
	else {
		/* throw error */
		printf("%d errors were encountered in file.\n", numerr);
		kill_list(thelist);
		return EXIT_FAILURE;
	}
}
